package com.train.common.aspect;

import com.alibaba.fastjson2.JSONObject;
import com.train.common.annotation.ResourceUrl;
import com.train.common.service.MinioService;
import com.train.common.utils.spring.SpringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 资源URL处理切面
 * 用于拦截控制器方法返回的对象，将带有@ResourceUrl注解的字段从objectName转换为临时URL
 *
 * @author train
 */
@Aspect
@Component
@Order(100)
public class ResourceUrlAspect {

    private static final Logger logger = LoggerFactory.getLogger(ResourceUrlAspect.class);

    private MinioService minioService;

    /**
     * 切入点：拦截所有控制器方法
     */
    @Pointcut("execution(* com.train.*.controller.*Controller.*(..))")
    public void resourceUrlPointCut() {
    }

    /**
     * 环绕通知：处理返回值中的资源URL
     */
    @Around("resourceUrlPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 执行原方法
        Object result = point.proceed();

        // 懒加载minioService
        if (minioService == null) {
            minioService = SpringUtils.getBean(MinioService.class);
        }

        // 处理返回结果中的资源URL
        processResult(result);

        return result;
    }

    /**
     * 处理返回结果中的资源URL
     *
     * @param result 返回结果
     */
    private void processResult(Object result) {
        if (result == null) {
            return;
        }

        // 处理AjaxResult类型的返回值
        if (result instanceof com.train.common.core.domain.AjaxResult) {
            com.train.common.core.domain.AjaxResult ajaxResult = (com.train.common.core.domain.AjaxResult) result;
            Object data = ajaxResult.get("data");
            if (data != null) {
                processObject(data);
            }
        } else if (result instanceof com.train.common.core.page.TableDataInfo) {
            // 处理TableDataInfo类型的返回值
            com.train.common.core.page.TableDataInfo tableDataInfo = (com.train.common.core.page.TableDataInfo) result;
            Object rows = tableDataInfo.getRows();
            if (rows != null) {
                processObject(rows);
            }
        } else if (result.getClass().getName().equals("com.train.course.vo.QueryVideoCourseVO")) {
            // 通过反射处理QueryVideoCourseVO类型的返回值，避免模块间直接依赖
            try {
                Object videoCourseVOList = result.getClass().getMethod("getVideoCourseVOList").invoke(result);
                if (videoCourseVOList != null) {
                    processObject(videoCourseVOList);
                }
            } catch (Exception e) {
                logger.error("处理QueryVideoCourseVO对象失败: {}", e.getMessage());
            }
        } else if (result instanceof Map) {
            // 处理Map类型
            Map<?, ?> map = (Map<?, ?>) result;
            for (Object value : map.values()) {
                processObject(value);
            }
        } else {
            // 处理其他类型
            processObject(result);
        }
    }

    /**
     * 处理对象中的资源URL
     *
     * @param obj 待处理对象
     */
    private void processObject(Object obj) {
        if (obj == null) {
            return;
        }

        // 处理集合类型
        if (obj instanceof List) {
            List<?> list = (List<?>) obj;
            for (Object item : list) {
                processObject(item);
            }
            return;
        }

        // 处理数组类型
        if (obj.getClass().isArray()) {
            Object[] array = (Object[]) obj;
            for (Object item : array) {
                processObject(item);
            }
            return;
        }

        // 处理JSON对象类型
        if (obj instanceof JSONObject) {
            JSONObject jsonObject = (JSONObject) obj;
            processJSONObject(jsonObject);
            return;
        }

        // 处理自定义对象类型
        processCustomObject(obj);
    }

    /**
     * 处理JSONObject中的资源URL
     *
     * @param jsonObject JSONObject对象
     */
    private void processJSONObject(JSONObject jsonObject) {
        for (String key : jsonObject.keySet()) {
            Object value = jsonObject.get(key);
            if (value instanceof String && !key.equals("url")) {
                // 这里简单处理，如果键名包含url相关字样，尝试转换
                if (key.toLowerCase().contains("url") || key.toLowerCase().contains("image")) {
                    try {
                        String objectName = (String) value;
                        if (objectName != null && !objectName.isEmpty() && !objectName.startsWith("http")) {
                            String presignedUrl = minioService.getPresignedUrl(objectName, 7200);
                            jsonObject.put(key, presignedUrl);
                        }
                    } catch (Exception e) {
                        logger.error("转换JSON对象中的URL失败: {}", e.getMessage());
                    }
                }
            } else {
                processObject(value);
            }
        }
    }

    /**
     * 处理自定义对象中的资源URL
     *
     * @param obj 自定义对象
     */
    private void processCustomObject(Object obj) {
        Class<?> clazz = obj.getClass();
        
        // 跳过 JDK 内部类，避免反射访问限制
        if (isJdkInternalClass(clazz)) {
            logger.debug("跳过 JDK 内部类: {}", clazz.getName());
            return;
        }
        
        List<Field> fields = getAllFields(clazz);

        for (Field field : fields) {
            // 检查字段是否有@ResourceUrl注解
            ResourceUrl annotation = field.getAnnotation(ResourceUrl.class);
            if (annotation != null && !annotation.skip()) {
                // 尝试设置字段可访问，如果失败则跳过（可能是 JDK 内部类字段）
                if (!trySetAccessible(field)) {
                    logger.debug("无法访问字段: {}，跳过处理", field.getName());
                    continue;
                }
                try {
                    Object value = field.get(obj);
                    if (value instanceof String) {
                            String objectName = (String) value;
                            if (objectName != null && !objectName.isEmpty() && !objectName.startsWith("http")) {
                                // 检查是否是逗号分隔的多个URL
                                if (objectName.contains(",")) {
                                    // 处理多图片URL列表
                                    String[] objectNames = objectName.split(",");
                                    StringBuilder presignedUrls = new StringBuilder();
                                    for (int i = 0; i < objectNames.length; i++) {
                                        String name = objectNames[i].trim();
                                        if (!name.isEmpty()) {
                                            String presignedUrl = minioService.getPresignedUrl(name, annotation.expires());
                                            if (i > 0) {
                                                presignedUrls.append(",");
                                            }
                                            presignedUrls.append(presignedUrl);
                                        }
                                    }
                                    field.set(obj, presignedUrls.toString());
                                } else {
                                    // 生成单个临时URL
                                    String presignedUrl = minioService.getPresignedUrl(objectName, annotation.expires());
                                    field.set(obj, presignedUrl);
                                }
                            }
                        } else if (value instanceof List) {
                        // 处理URL列表
                        List<?> list = (List<?>) value;
                        List<String> urlList = new ArrayList<>();
                        for (Object item : list) {
                            if (item instanceof String) {
                                String objectName = (String) item;
                                if (objectName != null && !objectName.isEmpty() && !objectName.startsWith("http")) {
                                    String presignedUrl = minioService.getPresignedUrl(objectName, annotation.expires());
                                    urlList.add(presignedUrl);
                                } else {
                                    urlList.add((String) item);
                                }
                            }
                        }
                        if (!urlList.isEmpty()) {
                            field.set(obj, urlList);
                        }
                    }
                } catch (Exception e) {
                    logger.error("转换字段[{}]的URL失败: {}", field.getName(), e.getMessage());
                }
            }
        }

        // 递归处理嵌套对象
        for (Field field : fields) {
            if (!field.getType().isPrimitive() && !field.getType().equals(String.class) &&
                    !field.getType().isArray() && !List.class.isAssignableFrom(field.getType()) &&
                    !Map.class.isAssignableFrom(field.getType())) {
                // 尝试设置字段可访问，如果失败则跳过（可能是 JDK 内部类字段）
                if (!trySetAccessible(field)) {
                    logger.debug("无法访问字段: {}，跳过递归处理", field.getName());
                    continue;
                }
                try {
                    Object nestedObj = field.get(obj);
                    if (nestedObj != null) {
                        processCustomObject(nestedObj);
                    }
                } catch (Exception e) {
                    logger.error("处理嵌套对象失败: {}", e.getMessage());
                }
            }
        }
    }

    /**
     * 获取类及其父类的所有字段
     *
     * @param clazz 类
     * @return 字段列表
     */
    private List<Field> getAllFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        while (clazz != null && clazz != Object.class) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass();
        }
        return fields;
    }

    /**
     * 判断是否为 JDK 内部类
     * 包括 java.*、javax.*、sun.* 等包下的类
     *
     * @param clazz 类
     * @return 是否为 JDK 内部类
     */
    private boolean isJdkInternalClass(Class<?> clazz) {
        if (clazz == null) {
            return true;
        }
        String className = clazz.getName();
        // 检查是否为 JDK 内部类
        return className.startsWith("java.") ||
               className.startsWith("javax.") ||
               className.startsWith("sun.") ||
               className.startsWith("com.sun.") ||
               className.startsWith("jdk.") ||
               className.startsWith("org.w3c.") ||
               className.startsWith("org.xml.") ||
               // 检查是否为代理类
               clazz.getName().contains("$$") ||
               // 检查是否为数组且元素类型是 JDK 内部类
               (clazz.isArray() && isJdkInternalClass(clazz.getComponentType()));
    }

    /**
     * 安全地尝试设置字段可访问
     * 如果字段属于 JDK 内部类，会捕获反射相关异常（包括 Java 17+ 的 InaccessibleObjectException）
     *
     * @param field 字段
     * @return 是否成功设置可访问
     */
    private boolean trySetAccessible(Field field) {
        try {
            // 检查字段所属的类是否为 JDK 内部类
            if (isJdkInternalClass(field.getDeclaringClass())) {
                return false;
            }
            field.setAccessible(true);
            return true;
        } catch (Exception e) {
            // 捕获所有异常，包括：
            // - Java 17+ 的 InaccessibleObjectException（继承自 ReflectiveOperationException）
            // - SecurityException
            // - 其他反射相关异常
            logger.debug("无法访问字段 {}: {}", field.getName(), e.getMessage());
            return false;
        }
    }
}